/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Controllers;

import Algorithms.BubbleSort;
import Algorithms.Comparaciones;
import Models.FacebookFriend;
import Models.MergedFriend;
import Models.TwitterFriend;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.jdom.*;

/**
 *
 * Controla las funciones que se realizan para unir los contactos entre Facebook y Twitter.<br> Genera la lista ordenada con los contactos mas cercanos.
 * @author Daniel Ochoa John
 */
public class MergedController {
    
    private TwitterController TC;
    private FacebookController FBC;
    private ArrayList TwitterUsage = new ArrayList();
    private ArrayList FacebookUsage = new ArrayList();
    private ArrayList<MergedFriend> ListaAmigos = new ArrayList();
    private ArrayList<MergedFriend> ListaAmigosOrdenada = new ArrayList();
    private ArrayList<FacebookFriend> FBFriends;
    private ArrayList<TwitterFriend> TWFriends;
    private float[][] track;
    
    
    /**
     * 
     * Constructor de la clase MergedController
     * 
     * @param TC El controlador de Twitter de la instancia actual.
     * @param FBC El controlador de Facebook de la instancia actual.
     */
    public MergedController(TwitterController TC,FacebookController FBC)
    {
        this.FBC = FBC;
        this.TC = TC;
        CapturarDatos();
    }
    
    
    /*
     * 
     * Analiza si en Twitter y Facebook existe la misma persona en base a un porcentaje de similitud de string definido. <br>
     * Si encuentra alguna coincidencia que iguale o supere la cota (0.67), se crea un contacto MergedFriend con los datos unidos. 
     * 
     */
    public void AnalizaDuplicados()
    {
        track = new float[FBFriends.size()][TWFriends.size()];
        
        for(int i=0;i<FBFriends.size();i++)
        {
            FacebookFriend aux = FBFriends.get(i);
            
            for(int j=0;j<TWFriends.size();j++)
            {
                TwitterFriend aux2 = TWFriends.get(j);
                
                if(track[i][j]== 0)
                    {    
                        float resultado = Comparaciones.CompararNombres(aux.getName().toLowerCase().trim(),aux2.getNombre().toLowerCase().trim());
                        if(resultado == 0)
                        {
                            track[i][j] = -1;
                        }
                        else
                        {
                            track[i][j] = Comparaciones.CompararNombres(aux.getName(),aux2.getNombre());
                        }
                        
                        
                        
                        if(resultado >= 0.67)
                        {
                            //System.out.println(aux.getName() + " V/S "+aux2.getNombre()+ " :"+ String.valueOf(resultado));
                            //AQUI DEBO UNIR LOS DATOS
                            TwitterUsage.add(j);
                            FacebookUsage.add(i);
                            MergedFriend auxm = this.UnirDatos(aux2,aux);
                            //System.out.println(auxm.getScore());
                            ListaAmigos.add(auxm);
                        }
                        
                    }
                
            }
            
        }
        
        AgregarNoDuplicados();
        OrdenarLista();
    }
    
    /**
     * 
     * Lee las listas de Twitter y Facebook para analizarlas.
     * 
     */
    private void CapturarDatos()
    {
        
        this.FBFriends = this.FBC.getFBFriendsM();
        this.TWFriends = this.TC.getTFriendsM();
    }
    
    
    /**
     * Une un amigo de Twitter y uno de Facebook en uno solo.<br>Este método se utiliza para unir amigos si es que se detecta algún contacto similar entre las dos redes sociales.
     * @param TF Amigo de Twitter
     * @param FF Amigo de Facebook
     * @return Un Objeto de la clase MergedFriend
     */
    private MergedFriend UnirDatos(TwitterFriend TF,FacebookFriend FF)
    {
        
        //DATOS
        String Name;
        long FacebookID;
        long TwitterID;
        String FBUsername;
        String TWUsername;
        String Description;
        String FBProfilePicture;
        String TWProfilePicture;
        String Sex;
        String BirthdayDate;
        String Relationship;
        float Score;
        FacebookID = FF.getID();
        TwitterID = TF.getID();
        Sex = FF.getSex();
        BirthdayDate = FF.getBirthdayDate();
        FBProfilePicture = FF.getProfilePicture();
        TWProfilePicture = TF.getURL();
        FBUsername = FF.getUsername();
        TWUsername = TF.getTwitterName();
        Description = TF.getDescripcion();
        Relationship = FF.getRelationship();
        
        if(TF.getNombre().length() >= FF.getName().length())
        {
            Name = TF.getNombre();
        }
        else
        {
            Name = FF.getName();
        }
        
        Score = this.CalcularScore(TF, FF);
        
        MergedFriend Merged = new MergedFriend(
                Name,
                FacebookID,
                TwitterID,
                FBUsername,
                TWUsername,
                Description,
                FBProfilePicture,
                TWProfilePicture,
                Sex,
                BirthdayDate,
                Relationship
                );
        
        Merged.setScore(Score);
        
        return Merged;
        
    }
    
    
    /**
     * 
     * Calcula el grado de relación que tiene un contacto en base a sus datos de Twitter y Facebook
     * 
     * @param TF Contacto en Twitter
     * @param FF Contacto en Facebook
     * @return El valor numérico del grado de relación.
     */
    private float CalcularScore(TwitterFriend TF,FacebookFriend FF)
    {
        float score1;
        float score2;
        
        score1 = (float) ((float)TF.getNumMentions() * 0.4);
        score2 = (float) ((float) ((float)FF.getCommonFriends()*0.2) + (Float.valueOf(FF.getMutualPhotos()) * 0.3) + ((float)FF.getCommonMessages() * 0.5));
    
        return (float) (score1 + (score2 * 0.6));
    }
    
    
    /**
     * 
     * Agrega los amigos que no tienen ningun duplicado en la otra red social a la lista final.
     * 
     */
    private void AgregarNoDuplicados()
    {
        for(int i=0;i<this.FBFriends.size();i++)
        {
            if(!this.FacebookUsage.contains(i))
            {
                this.ListaAmigos.add(this.AgregarFacebook(FBFriends.get(i)));
            }
        }
        
        for(int j=0;j<this.TWFriends.size();j++)
        {
            if(!this.TwitterUsage.contains(j))
            {
                this.ListaAmigos.add(this.AgregarTwitter(TWFriends.get(j)));
            }
        }   
    }
    
    
    /**
     * 
     * Agrega un amigo de Facebook que no tiene un símil en Twitter a la lista final
     * 
     * @param FF Amigo de Facebook
     * @return Un objeto MergedFriend con los datos de Facebook del contacto.
     */
    private MergedFriend AgregarFacebook(FacebookFriend FF)
    {
        //DATOS  
        String Name = null;
        long FacebookID = 0;
        long TwitterID = 0;
        String FBUsername = null;
        String TWUsername = "";
        String Description = "";
        String FBProfilePicture = null;
        String TWProfilePicture = null;
        String Sex = null;
        String BirthdayDate = null;
        String Relationship;
        float Score = 0;
        
        Name = FF.getName();
        FacebookID = FF.getID();
        TwitterID = 0;
        FBUsername = FF.getUsername();
        FBProfilePicture = FF.getProfilePicture();
        Sex = FF.getSex();
        BirthdayDate = FF.getBirthdayDate();
        Score = (float) ((float) ((float)FF.getCommonFriends()*0.2) + (Float.valueOf(FF.getMutualPhotos()) * 0.3) + ((float)FF.getCommonMessages() * 0.5));
        Relationship = FF.getRelationship();
        
        MergedFriend Merged = new MergedFriend(
                Name,
                FacebookID,
                TwitterID,
                FBUsername,
                TWUsername,
                Description,
                FBProfilePicture,
                TWProfilePicture,
                Sex,
                BirthdayDate,
                Relationship
                );
        
        Merged.setScore(Score);
        
        return Merged;
    }
    
    
    /**
     * 
     * Agrega un amigo de Twitter que no tiene un símil en Facebook a la lista final
     * 
     * @param TF Amigo de Twitter
     * @return Un objeto MergedFriend con los datos de Twitter del contacto.
     */
    private MergedFriend AgregarTwitter(TwitterFriend TF)
    {
        //DATOS  
        String Name = null;
        long FacebookID = 0;
        long TwitterID = 0;
        String FBUsername = null;
        String TWUsername = null;
        String Description = null;
        String FBProfilePicture = null;
        String TWProfilePicture = null;
        String Sex = null;
        String BirthdayDate = null;
        float Score = (float) ((float)TF.getNumMentions() * 0.4);
        
        Name = TF.getNombre();
        TwitterID = TF.getID();
        TWUsername = TF.getTwitterName();
        Description = TF.getDescripcion();
        TWProfilePicture = TF.getURL();
        
        MergedFriend Merged = new MergedFriend(
                Name,
                FacebookID,
                TwitterID,
                FBUsername,
                TWUsername,
                Description,
                FBProfilePicture,
                TWProfilePicture,
                Sex,
                BirthdayDate,
                "None"
                );
        
        Merged.setScore(Score);
        
        return Merged;
    }

    
    /**
     * 
     * Retorna una lista con los K-Contactos con los que se tiene una mayor relación a través de redes sociales.<br>
     * La lista se retorna en orden descendente, es decir, el contacto con mayor puntaje va primero.
     * 
     * @param K Entero que denota los K-Contactos mas cercanos que se extraerán en el proceso de unión.
     * @return Lista enlazada que contiene objetos MergedFriend.
     */
    public List<MergedFriend> getListaAmigos(int K)
    {
        
        List<MergedFriend> aux = null;
        int index = K;
        if(ListaAmigosOrdenada.size() < index)
        {     
            K = ListaAmigosOrdenada.size();
        }
        aux =  ListaAmigosOrdenada.subList(0, K);
        return aux;
        
    }
    
    
    /**
     * 
     * 
     * Retorna la lista enlazada con los K-Contactos con los que se tiene una mayor relación a través de redes sociales.
     * Según el genero establecido.<br>
     * La lista se retorna en orden descendente, es decir, el contacto con mayor puntaje va primero.
     * 
     * 
     * @param K Entero que denota los K-Contactos mas cercanos que se extraerán en el proceso de unión.
     * 
     * @param Preference Es un criterio de genero de los amigos del usuario. Los posibles valores son: <br>
     *  0 = Representa a las mujeres.<br>
     *  1 = Representa a los hombres.<br>
     *
     * 
     * @return Lista enlazada que contiene objetos MergedFriend. 
     */
    public List<MergedFriend> getListaAmigosSexo(int K,String Sexo)
    {
        List<MergedFriend> aux = new ArrayList();
        int index = K;
        if(ListaAmigosOrdenada.size() < index)
        {
            K = ListaAmigosOrdenada.size();
        }
        
        int hits = 0;
        
        for(int i=0;i<ListaAmigosOrdenada.size();i++)
        {
            if(hits == K)
            {
                break;
            }
            else
            {
                MergedFriend auxf = ListaAmigosOrdenada.get(i);
                
                if(auxf.getSex().equals(Sexo))
                {
                    aux.add(auxf);
                    hits++;
                }
            }
        }
        
        return aux;
    }
    
    
    /**
     * 
     * Retorna la lista enlazada con los K-Contactos con los que se tiene una mayor relación a través de redes sociales.
     * Según el criterio establecido.<br>
     * La lista se retorna en orden descendente, es decir, el contacto con mayor puntaje va primero.
     * 
     * @param K Entero que denota los K-Contactos mas cercanos que se extraerán en el proceso de unión.
     * 
     * @param Criterio Es un criterio de relación entre el usuario y el contacto. Los posibles valores son: <br>
     *  Amigo = Describe a Amigos de sexo masculino<br>
     *  Amiga = Describe a Amigos de sexo femenino<br>
     *  Faimlia = Describe a Amigos que son familia del usuario<br>
     *  Novio = Describe al Amigo que es novio del usuario<br>
     *  Novia = Describe a la Amiga que es novia del usuario<br>
     * 
     * @return Lista enlazada que contiene objetos MergedFriend.
     * 
     */
    public List<MergedFriend> getListaAmigos(String C,int K)
    {
        List<MergedFriend> aux = new ArrayList();
        int index = K;
        if(ListaAmigosOrdenada.size() < index)
        {
            K = ListaAmigosOrdenada.size();
        }
        
        int hits = 0;
        
        for(int i=0;i<ListaAmigosOrdenada.size();i++)
        {
            if(hits == K)
            {
                break;
            }
            else
            {
                MergedFriend auxf = ListaAmigosOrdenada.get(i);
                

                if(auxf.getRelationship().toLowerCase().trim().equals(C.toLowerCase().trim()))
                {
                    aux.add(auxf);
                    hits++;
                }
            }
        }
        
        return aux;
        
    }
    
    public List<MergedFriend> getListaAmigosCriterios(List<String> Criterios,int K)
    {
        List<MergedFriend> aux = new ArrayList();
        int index = K;
        if(ListaAmigosOrdenada.size() < index)
        {
            K = ListaAmigosOrdenada.size();
        }
        
        int hits = 0;
        
        for(int i=0;i<ListaAmigosOrdenada.size();i++)
        {
            if(hits == K)
            {
                break;
            }
            else
            {
                MergedFriend auxf = ListaAmigosOrdenada.get(i);
                
                String[] Relationships = auxf.getRelationship().split(",");
                
                for(int j = 0; j<Relationships.length; j++)
                {
                    String auxiliar = Relationships[j];
                    if(Criterios.contains(auxiliar))
                    {
                        aux.add(auxf);
                        hits++;
                        break;
                    }
                }
            }
        }
        
        return aux;
    }
    
    public Document ExportarAXML(int K)
    {
        if(ListaAmigosOrdenada.isEmpty())
        {
            return null;
        }
        else
        {
        Element Root = new Element("SocialNetworkMerger");
        int size = K;
        for(int i=0;i<size;i++)
        {
            MergedFriend auxiliar = this.ListaAmigosOrdenada.get(i);
            Element Amigo = new Element("Amigo");
            Element Grado = new Element("Grado_de_Relación").setText(String.valueOf(auxiliar.getScore()));
            Element Nombre = new Element("Nombre").setText(auxiliar.getName());
            Element FProfile = new Element("Foto_Facebook").setText(auxiliar.getFBProfilePicture());
            Element TProfile = new Element ("Foto_Twitter").setText(auxiliar.getTWProfilePicture());
            Element Lugar = new Element ("Lugar").setText(String.valueOf(i));
            Element FUsuario = new Element("Nombre_Facebook").setText(auxiliar.getFBUsername());
            Element TUsuario = new Element("Nombre_Twitter").setText(auxiliar.getTWUsername());
            Element Cumpleaños = new Element("Cumpleaños").setText(auxiliar.getBirthdayDate());
            Element TipoRelacion = new Element("Tipo_Relacion").setText(auxiliar.getRelationship());
            Element Genero = new Element("Sexo").setText(auxiliar.getSex());
            Amigo.addContent(Lugar);
            Amigo.addContent(Nombre);
            Amigo.addContent(FProfile);
            Amigo.addContent(TProfile);
            Amigo.addContent(Grado);
            Amigo.addContent(FUsuario);
            Amigo.addContent(TUsuario);
            Amigo.addContent(Cumpleaños);
            Amigo.addContent(TipoRelacion);
            Amigo.addContent(Genero);
            Root.addContent(Amigo);
        }
        Document doc = new Document(Root);
        return doc;
        }
    }

    /**
     * 
     * Ordena la lista de manera Decreciente en base al grado de relación que tienen los MergedFriends. Se ordena utilizando<br>
     * un algoritmo de BubbleSort.
     * 
     */
    private void OrdenarLista()
    {
        this.ListaAmigosOrdenada = BubbleSort.BubbleSort(ListaAmigos);
    }
}
